﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ColorClusteringSOM
{
    /// <summary>
    /// Trida ktera obsahuje funkce pro Delani Rough setu
    /// </summary>
    class RoughFunc
    {

        private double w_upper = -1;
        private double w_lower = -1;
        private double threshold = -1;

        private Helper help = null;


        public RoughFunc(double threshold, double lowerTh, double upperTh)
        {
            // TODO: Complete member initialization
            this.w_lower = lowerTh;
            this.w_upper = upperTh;
            this.threshold = threshold;
            help = new Helper();
        }



        public double W_upper { get { return this.w_upper; } }

        public double W_lower { get { return this.w_lower; } }




        /// <summary>
        ///  metoda pro prirazeni neuronu do obou aproximaci leadru
        ///  Jak do UPPER tak do LOWER
        ///  
        /// </summary>
        /// <param name="neuron"></param>
        /// <param name="clusters"></param>
        public void AssigningNeuronToBothAp(Neuron actualNeuron, List<NeuronRough> clusters)
        {
            //** 1. Vrati nejblizsi cluster leadra k danemu neuronu
            double minDistance = Double.MaxValue;

            NeuronRough winLeader = null;
            foreach (NeuronRough leader in clusters)
            {
               // double distance = help.EuklidDistancePREDELAT(actualNeuron, leader);
                double distance = help.EuklidDistanceBetweenNeurons(actualNeuron, leader);
                if (distance < minDistance)
                {
                    minDistance = distance;
                    winLeader = leader;
                }
            }

            bool flagUpper = false;
            //** 2. prirazeni neuronu clustrum
            foreach (NeuronRough leader in clusters)
            {
                if (!leader.Equals(winLeader))  //aby nepřiřadil sebe samého
                {
                    double distance = help.EuklidDistanceBetweenNeurons(actualNeuron, leader);
                    double ratio = distance - minDistance;


                    if (ratio <= threshold) //prirazeni do horni App actualniho leadra
                    {
                        leader.AddToUpperApp(new NeuronRough(actualNeuron, false));

                        flagUpper = true; //flag aby se nezapomelo prirazeni neuronu do horni app vitezneho leadra
                    }
                }
            }
            if (flagUpper) //neuron (vstup) byl prirazen do horni app nejakeho leadra
            {
                winLeader.AddToUpperApp(new NeuronRough(actualNeuron, false));
            }
            else // pouze vitezny leader ma neuron ve sve dolni app
            {
                winLeader.AddToLowerApp(new NeuronRough(actualNeuron, false));
            }
        }

      


        ///***double
        /// <summary>
        /// metoda pro vypocet centroidu SPRAVNA
        /// </summary>
        /// <param name="clusters"></param>
        /// <returns>Vrati true jestli se zmenily pozice center, false jestli zustaly stejne</returns>
        public bool CalculationNewCentroids(List<NeuronRough> clusters)
        {
            int dimension = 0;

            int changeCentr = 0;   
            foreach (NeuronRough leader in clusters)
            {
                dimension = leader.LeaderWeight.Length;
                //stare vahy leadra
                double[] oldWeights = leader.LeaderWeight;
                //nove vahy leadra
                double[] newWeights = null;
                List<NeuronRough> lowerApp = leader.LowerAppNeuronsArray;
                List<NeuronRough> upperApp = leader.UpperAppNeuronsArray;

                              


                if (lowerApp.Count == 0 && upperApp.Count == 0) //TODO: neco stimhle udelat
                {
                    Console.WriteLine("No neurons in any aproximations! Calculating a new centroids!");
                    //return true;
                    newWeights = oldWeights;
                    // asi by se mel vybrat novy cluster leader y noveho souboru
                    //return true; // proste aby nebylo centrum a tam nic
                }
                if (lowerApp.Count != 0 && upperApp.Count == 0) //pokud je neco v dolni a neni nic v horni
                {
                    newWeights = Helper.RoughtSum(lowerApp, w_lower, dimension);   //soucet vah * w_lower hodnota
                }
                else if (lowerApp.Count == 0 && upperApp.Count != 0)// pokud je neco v horni a nic v dolni
                {
                    newWeights = Helper.RoughtSum(upperApp, w_upper, dimension);   //soucet vah * w_upper hodnota
                }
                else if (lowerApp.Count != 0 && upperApp.Count != 0) // pokud v obou aproximacich neco je
                {
                    newWeights = Helper.SumArrays(Helper.RoughtSum(upperApp, w_upper, dimension), Helper.RoughtSum(lowerApp, w_lower, dimension));

                }
                bool flagSAME = true;

                for (int i = 0; i < newWeights.Length; i++)
                {
                    if (oldWeights[i] != newWeights[i])
                    {
                        flagSAME = false;
                    }
                }

                //pokud se vahy nezmenily
                if (flagSAME)
                {
                    //nalezen novy centroid
                    changeCentr++;

                }
                //Console.WriteLine(help.PrintWeights(leader.LeaderWeight, "old"));
                //Console.WriteLine(help.PrintWeights(newWeights, "new"));
                leader.LeaderWeight = newWeights;
                //Console.WriteLine("x=" +leader.X+" y="+ leader.Y);
                //Console.WriteLine("Changing Center's positions OLD=> [{0},{1}] NEW=> [{2} , {3}]  REAL=> [{4},{5}]", xCor, yCor, xCorNEW, yCorNEW, leader.X, leader.Y);



            }

            Console.WriteLine("clusters.Count={0} changeCentr={1}", clusters.Count, changeCentr);
            if (changeCentr == clusters.Count)
            {
                return false;
            }
            return true;
        }

        /// <summary>
        /// Vrati nejblizsi leadra k danemu neuronu
        /// </summary>
        /// <param name="actualNeuron"></param>
        /// <param name="clusters"></param>
        /// <returns></returns>
        private NeuronRough ClosestLeader(Neuron actualNeuron, List<NeuronRough> clusters)
        {
            double min = Double.MaxValue;

            NeuronRough winLeader = null;
            foreach (NeuronRough leader in clusters)
            {

                //double distance = help.EuklidDistancePREDELAT(actualNeuron, leader);
                double distance = help.EuklidDistanceBetweenNeurons(actualNeuron, leader);
                if (distance < min)
                {
                    min = distance;
                    winLeader = leader;

                }

            }

            return winLeader;
        }

        /// <summary>
        ///     Metoda pro prepocet souradnic . vraci double pole
        /// </summary>
        /// <param name="listAprox"></param>
        /// <param name="w"> jako W upper nebo lower</param>
        /// <returns></returns>
        private double[] NewCoordinateNEPOUZIVAT(List<NeuronRough> listAprox, double W)
        {
            double[] cord = { 0, 0 };

            foreach (NeuronRough n in listAprox)
            {
                cord[0] += n.X;
                cord[1] += n.Y;
            }

            int countNeurons = listAprox.Count;

            cord[0] = ((cord[0] / countNeurons) * W);
            cord[1] = ((cord[1] / countNeurons) * W);

            return cord;

        }

        /// <summary>
        ///     Metoda pro prepocet souradnic . vraci double pole
        /// </summary>
        /// <param name="listAprox"></param>
        /// <param name="w"> jako W upper nebo lower</param>
        /// <returns></returns>
        private int[] NewCoordinateIntNEPOUZIVAT(List<NeuronRough> listAprox, double W)
        {
            int[] cord = { 0, 0 };

            foreach (NeuronRough n in listAprox)
            {
                cord[0] += n.X;
                cord[1] += n.Y;
            }

            int countNeurons = listAprox.Count;

            double doubleX = (cord[0] / countNeurons) * W;
            double doubleY = (cord[1] / countNeurons) * W;

            cord[0] = (int)((cord[0] / countNeurons) * W);
            cord[1] = (int)((cord[1] / countNeurons) * W);

            Console.WriteLine("Coordinate X={0}  real coordinate={1}", cord[0], doubleX);
            Console.WriteLine("Coordinate Y={0}  real coordinate={1}", cord[1], doubleY);
            return cord;

        }





    }
}

